﻿#pragma once
#include "pch.h"
#include "framework.h"

// 创建 封装包的类
class CPacket{
public:
	// 构造函数，并初始化成员变量
	CPacket() :sHead(0), nLength(0), sCmd(0), sSum(0) {}

	// 复制构造函数 -》目的对类的对象进行类值传递
	CPacket(const CPacket& pack) {
		sHead = pack.sHead;
		nLength = pack.nLength;
		sCmd = pack.sCmd;
		strData = pack.strData;
		sSum = pack.sSum;
	}

	//解析数据构造函数
	CPacket(const BYTE* pData, size_t& nSize) {
		
		// 包头
		size_t i = 0;
		// 寻找包头字符
		for (; i < nSize; i++) {
			if (*(WORD*)(pData + i) == 0xFEFF) {
				sHead = *(WORD*)(pData + i);
				// 防止特殊情况，只有包头的数据包，后面没有数据就会导致问题
				i += 2;
				break;
			}
		}

		// 读取包的长度，包的长度过短证明这个包有问题 包头2个字节+包长度4+命令2
		if (i + 4 + 2 + 2 > nSize) {//包数据可能不全，或者包头未能全部接收到
			nSize = 0;
			return;
		}

		// 包长
		nLength = *(DWORD*)(pData + i); 
		i += 4;
		// 包特别长，被分解发送的情况
		if (nLength + i > nSize) {//包未完全接收到，就返回，解析失败
			nSize = 0;
			return;
		}

		// 命令
		sCmd = *(WORD*)(pData + i); i += 2;
		if (nLength > 4) {
			// 读取报数据 减去包头和命令
			strData.resize(nLength - 2 - 2);
			memcpy((void*)strData.c_str(), pData + i, nLength - 4);
			i += nLength - 4;
		}

		// 
		sSum = *(WORD*)(pData + i); 
		i += 2;
		WORD sum = 0;
		for (size_t j = 0; j < strData.size(); j++)
		{
			// 将字符转成byte
			sum += BYTE(strData[j]) & 0xFF;
		}
		// 包解析成功
		if (sum == sSum) {
			nSize = i;//head 2字节 length4 data...
			return;
		}
		// 解析失败
		nSize = 0;
	}


	~CPacket(){}

	// 运算符重载
	CPacket& operator= (const CPacket& pack) {
		if (this != &pack) {
			sHead = pack.sHead;
			nLength = pack.nLength;
			sCmd = pack.sCmd;
			strData = pack.strData;
			sSum = pack.sSum;
		}
		return *this;
	}

public:
	WORD sHead;//包头固定位 区分值是占两个字节，我们用0xFEFF 来表示，这个一般是经验之谈
	DWORD nLength;//包长度（从控制命令开始，到和校验结束）
	WORD sCmd;//控制命令
	std::string strData;//包数据
	WORD sSum;//包尾 ‘和校验’
	std::string strOut;//整个包的数据
};


class CServerSocket{
public:
	// 3.2通过静态函数去调用私有变量
	static CServerSocket* getInstance() {
		if (m_instance == NULL) {
			// 4.通过类来创建对象，需要对其进行delete，但是这里没有所以我们在创建一个类
			m_instance = new CServerSocket();
		}
		return m_instance;
	}

	// 5.创建连接
	bool InitSocket() {

		// 校验连接是否成功
		if (m_sock == -1) {
			return false;
		}
		sockaddr_in serv_adr;  // 地址
		memset(&serv_adr, 0, sizeof(serv_adr)); //初始化
		// AF = address family
		serv_adr.sin_family = AF_INET;
		// inaddr any意思是：这里监听服务器上(被控机)的多个IP，服务器可以存在多个IP的
		// s_addr是宏 = S_un.S_addr 
		serv_adr.sin_addr.s_addr = INADDR_ANY;
		serv_adr.sin_port = htons(9527); //端口

		// 2.绑定
		if (bind(m_sock, (sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)
		{
			return false;
		}

		// 3.监听
		// 这里由于 远控都是一对一的，所以参数是1
		if (listen(m_sock, 1)==-1)
		{
			return false;
		}
		
		return true;
	}

	//6. 获取客户端
	bool AcceptClient() {
		TRACE("enter AcceptClient \r\n");
		// 5.接受连接
		sockaddr_in client_adr;
		int cli_sz = sizeof(client_adr);
		m_client =accept(m_sock, (sockaddr*)&client_adr, &cli_sz);
		if (m_client == -1)
		{
			return false;
		}
		return true;
		
	}

	// 7.接收和发送信号
// 定义一个宏，作为配置的缓冲区大小，然后就不用逐个修改了
#define BUFFER_SIZE 4096
	int DealCommand() {
		if (m_client == -1)
		{
			return false;
		}
		// 命令打包，缓存读取 🐳难点缓冲区的处理 视频 57:00
		//char buffer[1024] = "";
		char* buffer = new char[BUFFER_SIZE]; // 缓存区是4K,一个包一般是1300个字节
		
		// 判断缓冲区大小是否足够
		if (buffer == NULL) {
			TRACE("内存不足！ \r\n");
			return -2;
		}

		memset(buffer, 0, BUFFER_SIZE);
		size_t index = 0;
		while (true)
		{
			// ?为什么用size_T
			size_t len = recv(m_client, buffer + index, BUFFER_SIZE - index, 0);
			if (len <= 0) {
				delete[]buffer;
				return -1;
			}
			// 处理发送过来的包命令
			TRACE("recv &d \r\n", len);
			index += len;
			len = index;
			m_packet = CPacket((BYTE*)buffer, len);
			if (len > 0) {
				//移动内存，去除包头
				memmove(buffer, buffer + len, BUFFER_SIZE - len);
				index -= len;
				delete[]buffer;
				return m_packet.sCmd;
			}

		}
	}

	// 8.发送命令
	bool Send(const char* pData, int nSize) {
		if (m_client == -1)
		{
			return false;
		}
		return send(m_client, pData, nSize, 0) > 0;
	}


// 3.1将构造函数、复制构造函数、重载等号 设为私有
private:
	SOCKET m_sock;
	SOCKET m_client;
	CPacket m_packet;
	CServerSocket& operator=(const CServerSocket& ss) {}
	//复制构造函数
	CServerSocket(const CServerSocket& ss) {
		m_sock = ss.m_sock;
		m_client = ss.m_client;
	}

	CServerSocket() {
		// INVALID_SOCKET 等同于 -1
		m_client = INVALID_SOCKET;
		// 构造函数调用
		if (InitSockEnv() == false) {
			// 提示
			MessageBox(NULL, _T("无法初始window套接字环境,请检查网络设备"), _T("初始化失败"), MB_OK | MB_ICONERROR);
			// 初始化window 环境失败直接退出
			exit(0);
		}
		// 5.1.创建socket套接字采用的是TCP协议
		m_sock = socket(PF_INET, SOCK_STREAM, 0);
	}

	~CServerSocket(){
		// 初始化环境清理
		WSACleanup();
		closesocket(m_sock);  //关闭socket
	}

	// 初始化window环境
	boolean InitSockEnv() {
		// socket套接字 window环境的初始化
		WSADATA data;
		// makeword版本的低的兼容更多平台
		if (WSAStartup(MAKEWORD(1, 1), &data) != 0) {
			return false;
		}
		return true;
	}
	// 释放内存
	static void releaseInstance() {
		if (m_instance!=NULL)
		{
			CServerSocket* tmp = m_instance;
			m_instance = NULL;
			delete tmp;
		}
	}

	// 3.2 创建变量
	static CServerSocket* m_instance;

	// 4.1 定义个helper类
	class CHelper {
	public:
		CHelper() {
			CServerSocket::getInstance();
		}
		~CHelper() {
			CServerSocket::releaseInstance();
		}
	};

	// 4.2 创建一个属性
	static CHelper m_helper;
};

// 声明一个外部变量，这样外面就能直接调用它
// extern CServerSocket server;